package com.suray.wcs.service.thread;

import com.google.common.collect.Sets;
import com.suray.commin.core.utils.SpringContextHolder;
import com.suray.wcs.service.core.path.api.RgvTaskAssign;
import com.suray.wcs.service.core.util.CommonUtil;
import com.suray.wcs.service.enums.NotificationTypeEnum;
import com.suray.wcs.service.system.bo.NotificationBO;
import com.suray.wcs.service.system.entity.TaskDB;
import com.suray.wcs.service.system.service.impl.NotificationServiceImpl;
import com.suray.wcs.service.system.service.impl.TaskDBServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class DBReadThreadUtil {
    private final TaskDBServiceImpl taskService = SpringContextHolder.getBean(TaskDBServiceImpl.class);
    private final RgvTaskAssign rgvTaskAssign = SpringContextHolder.getBean(RgvTaskAssign.class);
    private NotificationServiceImpl notificationService = SpringContextHolder.getBean(NotificationServiceImpl.class);
    private Logger log = LoggerFactory.getLogger(this.getClass());
    private static final Set<Integer> unAssignedRgvTask = Sets.newHashSet();

    private DBReadThreadUtil() {
    }

    public static DBReadThreadUtil getInstance() {
        return DBReadUtilSingleton.INSTANCE;
    }

    @PostConstruct
    public void startDBThread() {
        new Thread(() -> fireTaskQueue(), "任务预分配线程").start();
    }

    /**
     * 任务预分配
     */
    private void fireTaskQueue() {
        List<TaskDB> relatedTaskList;
        List<TaskDB> taskDBList;
        while (true) {
            try {
                taskDBList = taskService.getPreProcessingRgvNoIsNull();
                if (CollectionUtils.isNotEmpty(taskDBList)) {
                    Iterator<TaskDB> it = taskDBList.iterator();
                    while (it.hasNext()) {
                        TaskDB taskDB = it.next();
                        it.remove();
                        try {
                            log.info("当前任务，开始预分配车辆" + taskDB);
                            // 校验当前巷道里有没有未预分配的移库任务（终点在该巷道），如果有，且顺序在该任务之前，则这一巷道的任务全部跳过。
                            if (StringUtils.isNotBlank(taskDB.getGroupId())) {
                                List<TaskDB> unPreAssignTransferTaskList = taskService.findTasksByGroupIdSecond(taskDB.getTaskId(), taskDB.getGroupId());
                                if (CollectionUtils.isNotEmpty(unPreAssignTransferTaskList)) {
                                    taskService.updateTaskTipsById(taskDB, "当前巷道存在没有未预分配的任务且顺序在该任务之前， 任务Id:" +
                                            String.join(",", unPreAssignTransferTaskList.stream().map(x -> x.getTaskId().toString()).collect(Collectors.toList())));
                                    processNotification(unAssignedRgvTask, taskDB.getTaskId());
                                    relatedTaskList = getRelatedTaskList(taskDB);
                                    if (CollectionUtils.isNotEmpty(relatedTaskList)) {
                                        relatedTaskList.remove(taskDB);
                                        taskDBList.removeAll(relatedTaskList);
                                        it = taskDBList.iterator();
                                    }
                                }
                            }
                            // 任务预分车
                            if (rgvTaskAssign.bindAvailableRgvWithTask(taskDB) == null) {
                                processNotification(unAssignedRgvTask, taskDB.getTaskId());
                                relatedTaskList = getRelatedTaskList(taskDB);
                                if (CollectionUtils.isNotEmpty(relatedTaskList)) {
                                    relatedTaskList.remove(taskDB);
                                    taskDBList.removeAll(relatedTaskList);
                                    it = taskDBList.iterator();
                                }
                            } else {
                                unAssignedRgvTask.remove(taskDB.getTaskId());
                            }
                        } catch (Exception e) {
                            log.error("任务预分配线程出现异常!任务taskId={}", taskDB.getTaskId(), e);
                            relatedTaskList = getRelatedTaskList(taskDB);
                            if (CollectionUtils.isNotEmpty(relatedTaskList)) {
                                relatedTaskList.remove(taskDB);
                                taskDBList.removeAll(relatedTaskList);
                                it = taskDBList.iterator();
                            }
                        }
                    }
                }
                Thread.sleep(1000);
            } catch (Exception e) {
                log.error("任务预分配线程出现异常", e);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    log.error("任务预分配线程出现异常", ex);
                }
            }
        }
    }

    private void processNotification(Set<Integer> unAssigedRgvTask, Integer taskId) {
        if (!unAssigedRgvTask.contains(taskId)) {
            saveNotification(taskId);
            unAssigedRgvTask.add(taskId);
        }
    }

    /**
     * 添加消息提示
     *
     * @param taskId
     */
    private void saveNotification(Integer taskId) {
        NotificationBO notificationBO = new NotificationBO();
        notificationBO.setContent(String.format("任务%s分配不到执行车辆", taskId));
        notificationBO.setNotificationType(NotificationTypeEnum.WARN.getType());
        notificationBO.setIsRead(false);
        notificationBO.setSeverityLevel(1);
        notificationService.insertNotification(notificationBO);
    }

    /**
     * 相关联任务（未分配）
     */
    private List<TaskDB> getRelatedTaskList(TaskDB taskDB) {
        // 同一巷道的必须按顺序分，一个未分，则相关联的全部跳过
        return taskService.findTasksBySingleRgvFlag(taskDB.getGroupId(), CommonUtil.PRE_PROCESSING_RGV_NO_NULL_PROCESSING_RGV_NULL);
    }

    private static class DBReadUtilSingleton {
        private static final DBReadThreadUtil INSTANCE = new DBReadThreadUtil();

    }
}
